//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ProvenanceSubscriberMessageParser
{
    using Microsoft.Research.eResearch.Blackboard.Common;
    using Microsoft.Research.ProvenanceSubscriberCommon.Models;
    using Microsoft.Research.eResearch.Common.Linq;
    using System;
    using System.Linq;
    using System.Globalization;

    public abstract class AbstractMessageParser
    {
        protected BlackboardMessageParser BlackboardMessageParser { get; private set; }

        protected AbstractMessageParser(BlackboardMessageParser blackboardMsgParser)
        {
            this.BlackboardMessageParser = blackboardMsgParser;
        }

        public abstract void Parse(BlackboardMessage message);

        #region Public static methods.

        /// <summary>
        /// Message parser for checking workflow created event
        /// </summary>
        /// <param name="message">instance of blackboard message</param>
        /// <returns>true if the message contains workflow created event, false otherwise</returns>
        internal static bool IsWorkflowInstanceCreated(BlackboardMessage message)
        {
            return message.ContainsKey("trident:WorkflowState") && message["trident:WorkflowState"] == "Created";
        }

        /// <summary>
        /// Message parser for checking workflow completed event
        /// </summary>
        /// <param name="message">instance of blackboard message</param>
        /// <returns>true if the message contains workflow completed event, false otherwise</returns>
        internal static bool IsWorkflowInstanceCompleted(BlackboardMessage message)
        {
            return message.ContainsKey("trident:WorkflowState") && message["trident:WorkflowState"] == "Completed";
        }

        /// <summary>
        /// Message parser for checking activity property value event
        /// </summary>
        /// <param name="message">instance of blackboard message</param>
        /// <returns>true if the message contains activity property and value, false otherwise</returns>
        internal static bool IsInputOutputEvent(BlackboardMessage message)
        {
            return message.ContainsKey("trident:FieldName") &&
                (message["trident:ActivityPropertyDirection"] == "Input" ||
                message["trident:ActivityPropertyDirection"] == "Output" ||
                message["trident:ActivityPropertyDirection"] == "InOut");
        }

        /// <summary>
        /// Message parser for checking resource event
        /// </summary>
        /// <param name="message">instance of blackboard message</param>
        /// <returns>true if the message contains activity property and value, false otherwise</returns>
        internal static bool IsResourceEvent(BlackboardMessage message)
        {
            return message.Keys.FirstOrDefault(key => (key == "trident:CpuResource" || key == "trident:MemoryResource")) != null;
        }

        /// <summary>
        /// Message parser for checking workflow event
        /// </summary>
        /// <param name="message">instance of blackboard message</param>
        /// <returns>true if the message contains activity property and value, false otherwise</returns>
        internal static bool IsWorkflowEvent(BlackboardMessage message)
        {
            return message.ContainsKey("trident:WorkflowState");
        }

        /// <summary>
        /// Message parser for checking activity event
        /// </summary>
        /// <param name="message">instance of blackboard message</param>
        /// <returns>true if the message contains activity property and value, false otherwise</returns>
        internal static bool IsActivityEvent(BlackboardMessage message)
        {
            return (message.ContainsKey("trident:ActivityState") && !message.ContainsKey("trident:FieldName"));
        }

        /// <summary>
        /// Message parser for checking workflow event
        /// </summary>
        /// <param name="message">instance of blackboard message</param>
        /// <returns>true if the message contains activity property and value, false otherwise</returns>
        internal static bool IsWorkflowMessage(BlackboardMessage message)
        {
            return message.ContainsKey("trident:WorkflowName");
        }

        /// <summary>
        /// Message parser for checking activity event
        /// </summary>
        /// <param name="message">instance of blackboard message</param>
        /// <returns>true if the message contains activity property and value, false otherwise</returns>
        internal static bool IsActivityMessage(BlackboardMessage message)
        {
            return message.ContainsKey("trident:ActivityName");
        }

        internal static bool IsActivityEventOrder(BlackboardMessage message)
        {
            return message.ContainsKey("trident:EventOrder");
        }

        /// <summary>
        /// Message parser for checking Iteration event.
        /// </summary>
        /// <param name="message">instance of blackboard message</param>
        /// <returns>true if the message contains user data, false otherwise</returns>
        internal static bool IsIterationEvent(BlackboardMessage message)
        {
            return message.ContainsKey("trident:IterationValue");
        }

        /// <summary>
        /// Used to get the Base information which is required by all parsers.
        /// </summary>
        /// <param name="message">instance of blackboard message.</param>
        /// <returns></returns>
        internal static ProvenanceInformationModel GetProvenanceInfo(BlackboardMessage message)
        {
            ProvenanceInformationModel provenenceModel = new ProvenanceInformationModel
            {
                JobId = message["trident:JobId"],
            };

            if (IsActivityMessage(message))
            {
                provenenceModel.ActivityName = message["trident:ActivityName"];
                if (message.ContainsKey("trident:ActivityLabel"))
                {
                    provenenceModel.Label = message["trident:ActivityLabel"];
                }

                provenenceModel.IsWorkflowEvent = false;
            }

            if (IsWorkflowMessage(message))
            {
                provenenceModel.WorkflowName = message["trident:WorkflowName"];
                provenenceModel.Label = message["trident:WorkflowName"];
                provenenceModel.ActivityName = provenenceModel.WorkflowName;
                provenenceModel.IsWorkflowEvent = true;

                DateTime executionTime;
                try
                {
                    executionTime = new DateTime(long.Parse(message["time:DateTimeDescription"], CultureInfo.InvariantCulture));
                    provenenceModel.ExecutionTime = executionTime;
                }
                catch (FormatException)
                {
                    //Ignore. 
                }
                catch (ArgumentNullException)
                {
                    //Ignore. 
                }
                catch (OverflowException)
                {
                    //Ignore. 
                }
            }

            if (IsIterationEvent(message))
            {
                provenenceModel.CurrentIndex = message["trident:IterationValue"];
            }

            if (IsActivityEventOrder(message))
            {
                int eventOrder;

                if (!int.TryParse(message["trident:EventOrder"], out eventOrder))
                {
                    eventOrder = -1;
                }

                if (!IsClosedActivityEvent(message))
                {
                    provenenceModel.StartEventOrder = eventOrder;
                }
                else
                {
                    provenenceModel.EndEventOrder = eventOrder;
                }
            }

            return provenenceModel;
        }

        /// <summary>
        /// Determines whether current state is executing event as per the message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <returns>
        /// 	<c>true</c> if executing activity event; otherwise, <c>false</c>.
        /// </returns>
        internal static bool IsExecutingActivityEvent(BlackboardMessage message)
        {
            return message.ContainsKey("trident:ActivityState") && message["trident:ActivityState"] == "Executing";
        }

        /// <summary>
        /// Determines whether current state is closed event as per the message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <returns>
        /// 	<c>true</c> if closed activity event; otherwise, <c>false</c>.
        /// </returns>
        internal static bool IsClosedActivityEvent(BlackboardMessage message)
        {
            return message.ContainsKey("trident:ActivityState") && message["trident:ActivityState"] == "Closed";
        }

        /// <summary>
        /// Message parser for checking dataproduct event
        /// </summary>
        /// <param name="message">instance of blackboard message</param>
        /// <returns>true if the message contains user data, false otherwise</returns>
        internal static bool IsDataproductEvent(BlackboardMessage message)
        {
            return message.ContainsKey("trident:DataProductID");
        }

        /// <summary>
        /// Determines whether [is output data product event] [the specified message].
        /// </summary>
        /// <param name="message">The message.</param>
        /// <returns>
        /// 	<c>true</c> if [is output data product event] [the specified message]; otherwise, <c>false</c>.
        /// </returns>
        internal static bool IsOutputDataProductEvent(BlackboardMessage message)
        {
            return message.ContainsKey("trident:OutputDataProductID");
        }

        #endregion
    }
}
